package org.apache.lucene.search.highlight;

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.util.HashMap;
import java.util.HashSet;

import org.apache.lucene.analysis.Token;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Query;

/**
 * {@link Scorer} implementation which scores text fragments by the number of
 * unique query terms found. This class uses the {@link QueryTermExtractor}
 * class to process determine the query terms and their boosts to be used.
 * 
 * @author mark@searcharea.co.uk
 */
// TODO: provide option to boost score of fragments near beginning of document
// based on fragment.getFragNum()
public class QueryScorer implements Scorer {
	TextFragment currentTextFragment = null;
	HashSet uniqueTermsInFragment;
	float totalScore = 0;
	float maxTermWeight = 0;
	private HashMap termsToFind;

	/**
	 * 
	 * @param query
	 *            a Lucene query (ideally rewritten using query.rewrite before
	 *            being passed to this class and the searcher)
	 */
	public QueryScorer(Query query) {
		this(QueryTermExtractor.getTerms(query));
	}

	/**
	 * 
	 * @param query
	 *            a Lucene query (ideally rewritten using query.rewrite before
	 *            being passed to this class and the searcher)
	 * @param fieldName
	 *            the Field name which is used to match Query terms
	 */
	public QueryScorer(Query query, String fieldName) {
		this(QueryTermExtractor.getTerms(query, false, fieldName));
	}

	/**
	 * 
	 * @param query
	 *            a Lucene query (ideally rewritten using query.rewrite before
	 *            being passed to this class and the searcher)
	 * @param reader
	 *            used to compute IDF which can be used to a) score selected
	 *            fragments better b) use graded highlights eg set font color
	 *            intensity
	 * @param fieldName
	 *            the field on which Inverse Document Frequency (IDF)
	 *            calculations are based
	 */
	public QueryScorer(Query query, IndexReader reader, String fieldName) {
		this(QueryTermExtractor.getIdfWeightedTerms(query, reader, fieldName));
	}

	public QueryScorer(WeightedTerm[] weightedTerms) {
		termsToFind = new HashMap();
		for (int i = 0; i < weightedTerms.length; i++) {
			WeightedTerm existingTerm = (WeightedTerm) termsToFind
					.get(weightedTerms[i].term);
			if ((existingTerm == null)
					|| (existingTerm.weight < weightedTerms[i].weight)) {
				// if a term is defined more than once, always use the highest
				// scoring weight
				termsToFind.put(weightedTerms[i].term, weightedTerms[i]);
				maxTermWeight = Math.max(maxTermWeight, weightedTerms[i]
						.getWeight());
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.lucene.search.highlight.FragmentScorer#startFragment(org.apache.lucene.search.highlight.TextFragment)
	 */
	public void startFragment(TextFragment newFragment) {
		uniqueTermsInFragment = new HashSet();
		currentTextFragment = newFragment;
		totalScore = 0;

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.lucene.search.highlight.FragmentScorer#scoreToken(org.apache.lucene.analysis.Token)
	 */
	public float getTokenScore(Token token) {
		String termText = token.termText();

		WeightedTerm queryTerm = (WeightedTerm) termsToFind.get(termText);
		if (queryTerm == null) {
			// not a query term - return
			return 0;
		}
		// found a query term - is it unique in this doc?
		if (!uniqueTermsInFragment.contains(termText)) {
			totalScore += queryTerm.getWeight();
			uniqueTermsInFragment.add(termText);
		}
		return queryTerm.getWeight();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.lucene.search.highlight.FragmentScorer#endFragment(org.apache.lucene.search.highlight.TextFragment)
	 */
	public float getFragmentScore() {
		return totalScore;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.lucene.search.highlight.FragmentScorer#allFragmentsProcessed()
	 */
	public void allFragmentsProcessed() {
		// this class has no special operations to perform at end of processing
	}

	/**
	 * 
	 * @return The highest weighted term (useful for passing to
	 *         GradientFormatter to set top end of coloring scale.
	 */
	public float getMaxTermWeight() {
		return maxTermWeight;
	}
}
